#include "sockets_imp.hpp"
#include <cstdlib>

using namespace std;

UdpSocket::UdpSocket()
{
	data = new SocketData();
	data->handler = fromNative( ::socket(PF_INET, SOCK_DGRAM, 0) );
	if ( !isValid() )
	{
		delete data;
		throw SocketException( getsyserror() );
	}
}

UdpSocket::UdpSocket( UdpSocket const& udpSocket ) : data(udpSocket.data)
{
	data->refCount++;
}

UdpSocket::~UdpSocket()
{
	try 
	{	
		if ( data->refCount > 0 ) --data->refCount;
		else
		{
			close();
			delete data;
		}
	}catch (...) // Glip told me to ignore >:O
	{} 
}

void UdpSocket::bind( int port ) const
{
	sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons((u_short)port);
	addr.sin_addr.s_addr = INADDR_ANY;
	memset(reinterpret_cast<char *>(addr.sin_zero), 0, sizeof(addr.sin_zero));

	if ( isError( ::bind( toNative( data->handler ), reinterpret_cast<sockaddr *>(&addr), sizeof(addr)) ) )
	{
		throw SocketException( getsyserror() );
	}
}

bool UdpSocket::isValid() const
{
	return toNative( data->handler ) != INVALID_SOCKET;
}

void UdpSocket::close()
{
	if( isValid() )
	{
		if ( isError( closeSocket( data->handler ) ) ) throw SocketException( getsyserror() );
		data->handler = fromNative( INVALID_SOCKET );
	}
}

void UdpSocket::sendTo( char* buffer, int length, NetAddress& dest ) const
{
	int result = ::sendto( toNative(data->handler), buffer, length, 0, reinterpret_cast<sockaddr*>(&dest.data), sizeof(struct sockaddr));
	//TODO HANDLE ERRORS
}

int UdpSocket::recvFrom( char* buffer, int length, NetAddress& source ) const
{
	socklen_t fromlen = sizeof(struct sockaddr);
	int result = ::recvfrom( toNative(data->handler), buffer, length, 0, reinterpret_cast<sockaddr*>(&source.data), &fromlen);
	return result;
}
